package com.onehouse.domain.checkIn.service;

import com.google.common.eventbus.EventBus;
import com.onehouse.domain.checkIn.adapter.repository.ICheckInOrderRepository;
import com.onehouse.domain.checkIn.adapter.repository.ICheckInRoomPriceRepository;
import com.onehouse.domain.checkIn.adapter.repository.ICheckInRoomRepository;
import com.onehouse.domain.checkIn.adapter.repository.ICheckInSubOrderRepository;
import com.onehouse.domain.checkIn.adapter.repository.IRoomDepositRepository;
import com.onehouse.domain.checkIn.adapter.repository.IRoomRateRepository;
import com.onehouse.domain.checkIn.model.entity.CheckInRoom;
import com.onehouse.domain.checkIn.model.entity.CheckInRooms;
import com.onehouse.domain.checkIn.model.valobj.BillStatus;
import com.onehouse.domain.checkIn.model.valobj.CheckInStatus;
import com.onehouse.domain.checkIn.model.valobj.RoomPriceMap;
import com.onehouse.domain.checkIn.adapter.event.CheckInPaymentPostMessageEvent;
import com.onehouse.domain.checkIn.model.valobj.RoomDeposit;
import com.onehouse.domain.checkIn.model.valobj.RoomRate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;


@Service
public class CheckInService implements ICheckInService {

    private static final Logger logger = LogManager.getLogger(CheckInService.class);
    private static CheckInRooms checkInRooms;

    @Autowired
    private ICheckInRoomPriceRepository roomPriceRepository;

    @Autowired
    private ICheckInRoomRepository roomRepository;

    @Autowired
    private ICheckInOrderRepository orderRepository;

    @Autowired
    private ICheckInSubOrderRepository subOrderRepository;

    @Autowired
    private IRoomDepositRepository roomDepositRepository;
    
    @Autowired
    private IRoomRateRepository roomRateRepository;

    @Autowired
    private EventBus eventBus;

    @Override
    public CheckInRooms startCheckin(CheckInRooms checkInRooms) {
        logger.info("开始入住登记流程");
        if(this.checkInRooms != null) {
            logger.error("存在未完成的入住登记");
            throw new RuntimeException("请先完成当前的入住登记");
        }
        if(this.roomsAvaliableValidate(checkInRooms)) {
            logger.info("房间可用性验证通过");
            this.checkInRooms = checkInRooms;
            checkInRooms.setCheckInStatus(CheckInStatus.CHECKING_IN);
            checkInRooms.getCheckInRoomList().stream().forEach(checkInRoom -> {
                roomRepository.changeRoomStatusToBooked(checkInRoom.getRoom().getRoomNumber());
                checkInRoom.getCheckInBill().setBillStatus(BillStatus.INITIATED);
            });
        } else {
            logger.error("房间可用性验证失败");
            throw new RuntimeException("房间可用性测试失败，请重新选择房间");
        }
        return this.update(this.checkInRooms);
    }

    private boolean roomsAvaliableValidate(CheckInRooms checkInRooms) {
        ArrayList<CheckInRoom> checkInRoomsArrayList = checkInRooms.getCheckInRoomList();
        boolean flag = true;
        for (CheckInRoom checkInRoom : checkInRoomsArrayList) {
            if(!"AVALIABLE".equals(roomRepository.queryRoomStatus(checkInRoom.getRoom().getRoomNumber()))) {
                flag = false;
                break;
            }
        }
        return flag;
    }

    @Override
    public CheckInRooms update(CheckInRooms checkInRooms) {
        // 用哪种方法更新？比对差异 or 重新来过？粗暴一点，直接重新赋值
        checkInRooms.setCheckInStatus(this.checkInRooms.getCheckInStatus());
        this.checkInRooms = checkInRooms;   //信息丢失

        // 缺少规则校验，需补充

        for (CheckInRoom checkInRoom : checkInRooms.getCheckInRoomList()) {
            RoomPriceMap roomPriceMap = new RoomPriceMap();
            roomPriceMap.setRoomTypeId(checkInRoom.getRoom().getRoomTypeId());
            roomPriceMap.setCheckInDate(checkInRoom.getCheckInDate());
            roomPriceMap.setCheckOutDate(checkInRoom.getCheckOutDate());
            for(LocalDate date = checkInRoom.getCheckInDate(); date.isBefore(checkInRoom.getCheckOutDate()); date = date.plusDays(1)) {
                roomPriceMap.put(date, roomPriceRepository.getPrice(checkInRoom.getRoom().getRoomTypeId(), date));
            }
            checkInRoom.refreshBill(roomPriceMap);
        }

        checkInRooms.caculateTotalAmount();

        return this.checkInRooms;
    }

    @Override
    public CheckInRooms refresh() {
        if(this.checkInRooms == null) {
            throw new RuntimeException("当前没有入住登记，请选择房间进行登记");
        }
        this.checkInRooms = this.update(this.checkInRooms);
        return this.checkInRooms;
    }

    @Override
    public String createPayment() {
        logger.info("开始创建支付订单");
        this.checkInRooms.setCheckInRoomsId(genrateCheckInRoomsId(this.checkInRooms.getCheckInRoomList().size()));
        this.checkInRooms.setCheckInStatus(CheckInStatus.TO_BE_PAID);
        
        for (CheckInRoom checkInRoom : checkInRooms.getCheckInRoomList()) {
            checkInRoom.getCheckInBill().setBillStatus(BillStatus.PROCESSING);
            checkInRoom.setCheckInRoomId(generateCheckInRoomId(checkInRoom.getRoom().getRoomNumber()));
            roomRepository.changeRoomStatusToBooked(checkInRoom.getRoom().getRoomNumber());
        }

        logger.info("保存入住订单信息，订单号: {}", this.checkInRooms.getCheckInRoomsId());
        orderRepository.doSaveOrder(checkInRooms);
        
        for (CheckInRoom checkInRoom : checkInRooms.getCheckInRoomList()) {
            subOrderRepository.doSaveOrder(checkInRoom, checkInRooms.getCheckInRoomsId());
            
            RoomDeposit deposit = new RoomDeposit();
            deposit.setAmount(checkInRoom.getCheckInBill().getRoomDeposit().getAmount());
            deposit.setPaymentId(checkInRoom.getCheckInBill().getRoomDeposit().getPaymentId());
            roomDepositRepository.saveDeposit(checkInRoom.getCheckInRoomId(), deposit);
            
            RoomRate rate = new RoomRate();
            rate.setAmount(checkInRoom.getCheckInBill().getRoomRate().getAmount());
            rate.setPaymentId(checkInRoom.getCheckInBill().getRoomRate().getPaymentId());
            roomRateRepository.saveRate(checkInRoom.getCheckInRoomId(), rate);
        }

        CheckInPaymentPostMessageEvent checkInPaymentPostMessageEvent = CheckInPaymentPostMessageEvent.builder()
                .checkInOrderId(checkInRooms.getCheckInRoomsId())
                .totalAmount(checkInRooms.getTotalAmount())
                .createTime(LocalDateTime.now()).build();

        logger.info("发送支付事件，订单号: {}, 总金额: {}", checkInRooms.getCheckInRoomsId(), checkInRooms.getTotalAmount());
        eventBus.post(checkInPaymentPostMessageEvent);

        return this.checkInRooms.getCheckInRoomsId();
    }

    // 业务代号-10,checkInRoomsId根据业务代码+时间戳+当前checkInRooms.checkInRoomList中的个数
    private String genrateCheckInRoomsId(int roomCount) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String dateStr = sdf.format(System.currentTimeMillis());
        return "10" + dateStr;
    }

    // 业务代号-11,checkInRoomsId根据业务代码+时间戳（yyyyMMddHHmmss）+当前四位RoomNumber，高位补0
    private String generateCheckInRoomId(String roomNumber) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String dateStr = sdf.format(System.currentTimeMillis());
        return "11" + dateStr + roomNumber;
    }

    @Override
    public void processSuccessPayment(String checkInOrderId, String paymentId) {
        logger.info("处理支付成功，订单号: {}, 支付ID: {}", checkInOrderId, paymentId);
        if(this.checkInRooms.getCheckInRoomsId().equals(checkInOrderId)){
            for (CheckInRoom checkInRoom : checkInRooms.getCheckInRoomList()) {
                checkInRoom.getCheckInBill().getRoomDeposit().setPaymentId(paymentId);
                checkInRoom.getCheckInBill().getRoomRate().setPaymentId(paymentId);
                checkInRoom.getCheckInBill().setBillStatus(BillStatus.COMPLETED);
                roomRepository.changeRoomStatusToCheckedIn(checkInRoom.getRoom().getRoomNumber());

                subOrderRepository.updateOrder(checkInRoom, checkInRooms.getCheckInRoomsId());
                roomDepositRepository.updateDeposit(checkInRoom.getCheckInRoomId(), paymentId);
                roomRateRepository.updateRate(checkInRoom.getCheckInRoomId(), paymentId);
            }
            checkInRooms.setCheckInStatus(CheckInStatus.COMPLETED);
            orderRepository.updateOrder(checkInRooms);
            logger.info("入住订单处理完成，订单号: {}", checkInOrderId);
        } else {
            logger.error("入住单号与支付单号不匹配，入住单号: {}, 支付单号: {}", this.checkInRooms.getCheckInRoomsId(), checkInOrderId);
            throw new RuntimeException("入住单号与支付单上的单号不一致");
        }
    }

    @Override
    public void processFailPayment(Exception e) {
        logger.error("支付处理失败", e);
        for (CheckInRoom checkInRoom : checkInRooms.getCheckInRoomList()) {
            checkInRoom.getCheckInBill().setBillStatus(BillStatus.FAILED);
            subOrderRepository.updateOrder(checkInRoom, checkInRooms.getCheckInRoomsId());
            roomRepository.changeRoomStatusToAvaliable(checkInRoom.getRoom().getRoomNumber());
        }
        this.checkInRooms.setCheckInStatus(CheckInStatus.CANCELLED);
        orderRepository.updateOrder(checkInRooms);
        throw new RuntimeException(e);
    }

    @Override
    public void clear() {
        for (CheckInRoom checkInRoom : checkInRooms.getCheckInRoomList()) {
            roomRepository.changeRoomStatusToAvaliable(checkInRoom.getRoom().getRoomNumber());
        }
        checkInRooms = null;
    }

}
